package com.forg.maven.plugin;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;

/**
 * 
 */
@Mojo(name = "replaceConfig", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true)
public class ConfigMojo extends AbstractMojo {
    /**
     * Location of the file.
     */
//    @Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true)
//    private File outputDirectory;

    @Parameter(property = "env" )
    private String env;

//    @Parameter(readonly = true, defaultValue = "${basedir}")
//    private File baseDir;

    @Parameter(readonly = true, defaultValue = "${project.packaging}")
    private String packaging;

    // ${project.build.directory}
    @Parameter(defaultValue = "WEB-INF/classes/", property = "target")
    private String targetDir;

    @Parameter(readonly = true, defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}")
    private File finalPackage;

    // target/class 目录
    // @Parameter(defaultValue = "${project.build.outputDirectory}")
//    @Parameter(defaultValue = "${project.build.directory}/${project.artifactId}")
//    private File resourcesDir;

    @Parameter(defaultValue = "${basedir}/conf/", property = "conf")
    private File confDir;

    //存储 待增加配置路径 和 替换文件的 映射关系
    Map<String, String> configMapping = null ;

    public void execute() throws MojoExecutionException {
    	
    	//仅支持 war和jar包
        if (!("war".equals(packaging) || "jar".equals(packaging))) {
            getLog().info("! not support packaging type.(only jar or war supported)");
            return;
        }

        //没有设置环境的情况下 什么都不做
        if (!isExistsDir(confDir) || env == null || env.length() == 0) {
            getLog().info("! config dir or env not set,do nothing.");
            return;
        }
        
        // 环境配置目录 比如  ${project.base}/conf/ + ${env}/
        confDir = new File(lastSeparator( lastSeparator(confDir.getAbsolutePath()) + env));
        configMapping = new HashMap<String, String>();

        //建立映射
        getAllConfigs(confDir);

        //未获取到配置 什么都不做
        if (configMapping.isEmpty()) {
            getLog().info("no configs found,do nothing.");
        }

        // 操作的临时jar文件
        File tmpJar = null;
        try {

            //目标jar
            JarInputStream jarInputStream = new JarInputStream(
                    new FileInputStream(finalPackage));

            // 创建 操作的临时jar文件
            tmpJar = File.createTempFile(Long.toString(System.nanoTime()),
                    ".jar");
            JarOutputStream jarOutputStream = new JarOutputStream(
                    new FileOutputStream(tmpJar), jarInputStream.getManifest());

            while (true) {
                JarEntry jarEntry = jarInputStream.getNextJarEntry();

                // 遍历完毕
                if (jarEntry == null) break;

                // 不处理目录
                if (jarEntry.isDirectory()) continue;

                String jarEntryPath = jarEntry.getName();
                if (configMapping.keySet().contains(jarEntryPath)) {
                    // 处理覆盖
                    jarOutputStream
                            .putNextEntry(new JarEntry(jarEntry.getName()));
                    String configFilePath = configMapping.remove(jarEntryPath);
                    IOUtil.copy(
                            new FileInputStream(configFilePath ),
                            jarOutputStream);
                    getLog().info("replace [" + jarEntryPath + "] to [" + configFilePath + "]");
                    
                } else {
                    // 复制原内容
                    jarOutputStream.putNextEntry(jarEntry);
                    IOUtil.copy(jarInputStream, jarOutputStream);
                }
            }

            // 处理新增
            if (!configMapping.isEmpty()) {

                for (String k : configMapping.keySet()) {
                    jarOutputStream.putNextEntry(new JarEntry(k));
                    IOUtil.copy(new FileInputStream(configMapping.get(k)),
                            jarOutputStream);
                    
                    getLog().info("add [" + k + "] from [" + configMapping.get(k) + "]");
                }
            }

            jarOutputStream.closeEntry();
            jarOutputStream.finish();
            jarOutputStream.close();
            jarInputStream.close();

            FileUtils.copyFile(tmpJar, finalPackage);
        } catch (Exception e) {
            getLog().error(e);
        } finally {
            try {
            	if (tmpJar != null)
            		FileUtils.forceDelete(tmpJar);
            } catch (IOException e) {
                getLog().error("delete tmp file error,file path=" + tmpJar.getAbsolutePath(),e );
            }
        }

    }

    private void getAllConfigs(File dir) {
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (isExistsFile(files[i])) {
                // file
                doGetConfigs(files[i]);
            } else if (isExistsDir(files[i])) {
                // dir
                getAllConfigs(files[i]);
            } else {
                // pass
            }
        }
    }

    private void doGetConfigs(File conf) {

        if (!isExistsFile(conf))
            return;

        String confPath = sameSeparator(conf.getAbsolutePath());

        configMapping.put(
                confPath.replace(sameSeparator(confDir.getAbsolutePath()) + "/",
                        targetDir),
                confPath);
    }

    private String sameSeparator(String path) {
        return path.replaceAll("\\\\", "/");
    }
    
    private String lastSeparator(String path) {
        String _path = sameSeparator(path);
        if(!_path.endsWith("/")){
            return _path + "/";
        }
        
        return _path;
    }

    private boolean isExistsFile(File f) {
        if (f == null) return false;
        return (f.exists() && f.isFile());
    }

    private boolean isExistsDir(File f) {
        if (f == null) return false;
        return (f.exists() && f.isDirectory());
    }

//    public static void main(String[] args) throws MojoExecutionException {
//        ConfigMojo m = new ConfigMojo();
//        m.confDir = new File("D:/workspace/mavenKest\\conf/");
//        m.packaging = "war";
//        m.targetDir = "WEB-INF/classes/";
//        m.finalPackage = new File(
//                "D:/workspace/mavenKest/target/mavenKest.war");
//        m.execute();
//    }
}
